package com.buka.utils;

import com.buka.anno.Col_name;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Date;

public class JDBCUtil {


    //封装查询方法
    public static <T> List<T> executeQuery(String sql, Class clazz, Object... objects) {
        //封装数据库结果集
        List<T> resultList = new ArrayList<>();
        Connection connection = JDBCConnect.getConnection();
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < objects.length; i++) {
                //第一个参数  问号位置 从1开始
                //第二个参数  要替换的值
                preparedStatement.setObject(i + 1, objects[i]);
            }

            resultSet = preparedStatement.executeQuery();
            //TODO 解析resultSet
            while (resultSet.next()) {
                //如果传递的是int 表示是当前行第几列的数据
                //如果传递的是String（字段名） 按字段名取出当前行的值
                //？ 每个表的字段数不相同，字段名也不相同，怎么取出参数值并设置到java对象

                //查询user表   查询的结果封装到user对象中
                //第一列  是user对象中的那个属性
                //使用  user对象的属性名 获取值  保证user对象的属性名和数据库字段名一致
                //合适的方式应该是使用属性名来获取数据
                //是以java对象中的属性名为主还是以数据库字段的名字为主？
                //应该使用java 对象的？为什么用java对象。如果说java mysql 字段完全一致。
                //  会有不一致的情况，最终使用的是java对象处理业务逻辑  数据库中有10个  java对象就1个

                //使用反射获取java类中的全部属性
                //对于查询语句，返回的字段不确定的，返回的类型也是不确定的。
                //可能是一个int double long  基本数据类型都是单列的，直接获取第一列数据即可
                Object object = null;
                if (clazz == int.class || clazz == Integer.class) {
                    object = resultSet.getObject(1);
                } else if (clazz == double.class || clazz == Double.class) {
                    object = resultSet.getObject(1);
                } else if (clazz == long.class || clazz == Long.class) {
                    object = resultSet.getObject(1);
                } else if (Map.class.isAssignableFrom(clazz)) {
//                    Map map = new HashMap();
//                    map.put("userName","张三");
//                    map.put("age",10);
                    //最终返回的List<Map>
                    //使用map接收，map的key 是mysql字段名，需要获取到当前结果集的全部字段

                    //返回当前结果集的列的个数
                    int columnCount = resultSet.getMetaData().getColumnCount();
                    Map map = new HashMap();
                    for (int i = 1; i <= columnCount; i++) {
                        String columnName = resultSet.getMetaData().getColumnName(i);//根据索引获取列名
                        Object colValue = resultSet.getObject(columnName);//根据列名获取值
                        map.put(columnName, colValue);
                    }
                    object = map;
                } else {
                    //自定义对象
                    //java 对象 又被叫做bean对象
                    //使用反射获取对象的全部属性
                    Field[] declaredFields = clazz.getDeclaredFields();
                    object = clazz.newInstance();
                    for (Field field : declaredFields) {
                        String name = field.getName();//获取属性名
                        Col_name annotation = field.getAnnotation(Col_name.class);
                        if (null != annotation) {
                            //如果有col_name注解，表示属性和字段名不一致，需要使用 注解中的值作为数据库字段名取值
                            name = annotation.fieldName();
                        }

                        //用属性名 （字段名）  resultSet中获取 字段值
                        Object fieldValue = resultSet.getObject(name);
                        //先创建对象
                        //使用set方法给属性赋值
                        char[] chars = name.toCharArray();
                        chars[0] -= 32;//首字母大写
                        Method method = clazz.getMethod("set" + new String(chars), field.getType());
                        method.invoke(object, fieldValue);

                    }
                }
                resultList.add((T) object);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            JDBCConnect.back(connection);
        }
        return resultList;

    }


    //isert update delte   Object...  三个点表示可变参数列表
    //可变参数列表可以传递任意个参数，用数组接收参数的  Object...   Object[]
    public static int executeUpdateByArr(String sql, Object... objects) {
        Connection connection = JDBCConnect.getConnection();
        PreparedStatement preparedStatement = null;

        try {
            //预处理器
            //insert into user (id,name,age) value (?,?,?)
            preparedStatement = connection.prepareStatement(sql);
            //设置参数
//            objects[0]  第一个问号
//            objects[1]  第二个问号
//            objects[2]  第三个问号
            for (int i = 0; i < objects.length; i++) {
                preparedStatement.setObject(i + 1, objects[i]);
            }
            //执行sql
            int i = preparedStatement.executeUpdate();
            return i;


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }

            JDBCConnect.back(connection);
        }

        return 0;
    }


    public static <T> int excuteUpdate(String sql, T t, Class<T> tClass) {
        Connection connection = JDBCConnect.getConnection();
        PreparedStatement preparedStatement = null;
        try {

            //创建SQL 执行器
            // select * from user where id = '1 or 1=1'"
            // select * from user where id = 1 or 1=1"                          orer
//            preparedStatement = connection.prepareStatement("insert  into user value (?,?,?,?,?,?,?,?,?,?)");
            preparedStatement = connection.prepareStatement(sql);
            //替换参数
//            tClass.
//                  int  doubleLong String  date Map  Object
            if (Map.class.isAssignableFrom(tClass)) {
                //linkedhashmap(保证插入顺序)
                //将传入进来的对象转换为map对象

                //key  字段名   value :   字段值
                Map<String, Object> elementMap = ((Map) t);
                Set<String> keySet = elementMap.keySet();
                int index = 1;//标记  表示的是第几个参数
                for (String key : keySet) {
                    preparedStatement.setObject(index, elementMap.get(key));
                    index++;
                }
            } else if (tClass.getName().contains("com.buka")) {
                // tClass 当前参数对象的类型 tClass.getName 全限定名  obj 全部属性值 需要用反射
                //insert into user (id,name,age) value (?,?)   user
                Field[] fields = tClass.getDeclaredFields();//反射获取对象的属性描述信息
                for (int i = 0; i < fields.length; i++) {
                    Field field = fields[i];//遍历获取第 i 个属性
                    String name = field.getName();//获取属性名字  id
                    //获取属性的值  通过属性名，获取get方法，调用get方法获取属性值
                    //属性名首字母变成大写
                    char[] chars = name.toCharArray();
                    chars[0] -= 32;
                    //获得到get方法
                    Method method = tClass.getMethod("get" + new String(chars));
                    Object invoke = method.invoke(t);
                    preparedStatement.setObject(i + 1, invoke);
                }


            } else {
                preparedStatement.setObject(1, t);
            }
            //执行  增删改用update
            int i = preparedStatement.executeUpdate();

            return i;

        } catch (Exception e) {
            e.printStackTrace();//打印异常栈
        } finally {


            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }


            //关闭连接
            try {
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

        return 0;
    }


    public static void main(String[] args) {

    }


}
